#!/usr/bin/env python
#-*- coding:utf-8 -*-
import os,sys
import time
from wwpy.util.Time import Time

def timer(func):
    def _timer(*args,**kwargs):
        t=Time()
        func_name=func.__name__
        now=t.timestamp()
        print "Func %s is started at %s." % (func_name,t.now())
        result=func(*args,**kwargs)
        end=t.timestamp()
        print "Func %s is ended at %s." % (func_name,t.now())
        print "Func %s's total cost is %ss." % (func_name,end-now)
        return result
    return _timer

def counter(func):
    count=[0]
    def _counter(*args,**kwargs):
        count[0]+=1
        func_name=func.__name__.capitalize()
        print "%s use %d count." % (func_name,count[0])
        result=func(*args,**kwargs)
        return result
    return _counter

def memorizer(func):
    cached={}
    def _memorize(*args,**kwargs):
        key=(args,str(kwargs))
        if key not in cached:
            cached[key]=func(*args,**kwargs)
        return cached[key]
    return _memorize

def watcher(func):
    seen={}
    def watch(*args,**kwargs):
        key=(args,str(kwargs))
        if key not in seen:
            result=func(*args,**kwargs)
            seen[key]=result
        else:
            result=seen[key]
        return result
    return watch

def looper(mode='DAY'):
    mode=mode.upper()
    modes=['DAY','HOUR','DH']
    assert mode in modes,"Mode '%s' not in %s " % (mode,modes)
    
    def loop(func):
        t=Time()
        def loop_day(func,start_date,end_date,args,kwargs):
            date=int(start_date)
            start_date,end_date=int(start_date),int(end_date)
            while date>=start_date and date<=end_date:
                func(date,*args,**kwargs)
                date=int(t.date_add(date))
        def date_range(*args,**kwargs):
            start_date,end_date=None,None
            args=list(args)
            arg_num=len(args)
            if arg_num>=1:
                if arg_num==1:
                    date=args[0]
                    if t.is_date(date):
                        start_date=end_date=date
                        args.pop(0)
                elif arg_num>=2:
                    start,end=args[0],args[1]
                    if t.is_date(start):
                        start_date=start
                        args.pop(0)
                    if start_date and t.is_date(end):
                        end_date=end
                        args.pop(0)
                    else:
                        end_date=start_date
                if start_date and end_date:
                    return start_date,end_date,tuple(args),kwargs
            argv_num=len(sys.argv)
            if argv_num==1:
                start_date=end_date=t.yesterday(sep='')
            else:
                if argv_num==2:
                    start_date=end_date=t.date_format(sys.argv[1],sep='')
                elif argv_num>=3:
                    start_date,end_date=t.date_format(sys.argv[1],sep=''),t.date_format(sys.argv[2],sep='')
            return start_date,end_date,tuple(args),kwargs
        def loop_by_day(*args,**kwargs):
            start_date,end_date,args,kwargs=date_range(*args,**kwargs)
            loop_day(func,start_date,end_date,args,kwargs)
        def loop_hour(func,date,start_hour,end_hour,args,kwargs):
                start_hour=int(start_hour)
                end_hour=int(end_hour)
                assert start_hour>=0 and end_hour<=23,'Hour must be in range(0,24).'
                for hour in range(start_hour,end_hour+1):
                    hour=t.hour_format(hour)
                    func(date,hour,*args,**kwargs)
        def loop_by_hour(*args,**kwargs):
            date,start_hour,end_hour=None,None,None
            args=list(args)
            arg_num=len(args)
            if arg_num>=1:
                if arg_num==1:
                    date=args[0]
                    if t.is_date(date):
                        date=date
                        start_hour=end_hour=t.last_hour()
                        args.pop(0)
                    if not date:
                        return
                elif arg_num==2:
                    date,start=args[0],args[1]
                    if t.is_date(date):
                        date=date
                        args.pop(0)
                    if not date:
                        return 
                    if date and t.is_hour(start):
                        start_hour=end_hour=start
                        args.pop(0)
                    else:
                        end_hour=start_hour
                elif arg_num>=3:
                    date,start,end=args[0:3]
                    if t.is_date(date):
                        date=date
                        args.pop(0)
                    if not date:
                        return
                    if t.is_hour(start):
                        start_hour=start
                        args.pop(0)
                    else:
                        start_hour=t.last_hour()
                    if t.is_hour(end):
                        end_hour=end
                        args.pop(0)
                    else:
                        end_hour=start_hour
                if date and start_hour and end_hour:
                    loop_hour(func,date,start_hour,end_hour,tuple(args),kwargs)
                    return
            argv_num=len(sys.argv)
            if argv_num==1:
                date=t.yesterday(sep='')
                start_hour=end_hour=t.last_hour()
            elif argv_num==2:
                date=t.date_format(sys.argv[1],sep='')
                start_hour=end_hour=t.last_hour()
            elif argv_num==3:
                date=t.date_format(sys.argv[1],sep='')
                start_hour=end_hour=t.hour_format(sys.argv[2])
            else:
                date=t.date_format(sys.argv[1],sep='')
                start_hour=t.hour_format(sys.argv[2])
                end_hour=t.hour_format(sys.argv[3])
            loop_hour(func,date,start_hour,end_hour,tuple(args),kwargs)
        def loop_dh(func,start_date,end_date,args,kwargs):
            date=int(start_date)
            start_date,end_date=int(start_date),int(end_date)
            while date>=start_date and date<=end_date:
                for hour in range(0,24):
                    hour=t.hour_format(hour)
                    func(date,hour,*args,**kwargs)
                date=int(t.date_add(date,sep=''))
        def loop_by_dh(*args,**kwargs):
            start_date,end_date,args,kwargs=date_range(*args,**kwargs)
            loop_dh(func,start_date,end_date,args,kwargs)
            
        loop_dict=dict(
            DAY=loop_by_day,
            HOUR=loop_by_hour,
            DH=loop_by_dh,
        )
        def run(*args,**kwargs):
            if mode in loop_dict:
                func=loop_dict[mode]
                func(*args,**kwargs)
        return run
    return loop

def capturer(mode='stdout'):
    mode=mode.lower()
    assert mode in ('stdout','stderr'),"Mode must be 'stdout' or 'stderr'"
    from wwpy.util.Module import StringIO
    def _capture(func):
        def __capture_stdout(*args,**kwargs):
            out=StringIO.StringIO()
            oldout=sys.stdout
            sys.stdout=out
            try:
                result=func(*args,**kwargs)
            finally:
                sys.stdout=oldout
            return out.getvalue(),result
        def __capture_stderr(*args,**kwargs):
            out=StringIO.StringIO()
            oldout=sys.stdout
            sys.stderr=out
            try:
                result=func(*args,**kwargs)
            finally:
                sys.stderr=oldout
            return out.getvalue(),result
        if mode=='stdout':
            return __capture_stdout
        elif mode=='stderr':
            return __capture_stderr
    return _capture

def runner(is_join=False,mode='T'):
    assert mode in ('P','T'),"mode must be 'P' or 'T'"
    def _run(func):
        result=[]
        def wrap_func(*args,**kwargs):
            result.append(func(*args,**kwargs))
        def run_threads(*args,**kwargs):
            import threading
            thread=threading.Thread(target=wrap_func,args=args,kwargs=kwargs)
            thread.start()
            if is_join:
                thread.join()
                return result[0]
            else:
                return None if len(result)==0 else result[0]
        def run_processes(*args,**kwargs):
            import multiprocessing
            process=multiprocessing.Process(target=wrap_func,args=args,kwargs=kwargs)
            process.start()
            if is_join:
                process.join()
                return result[0]
            else:
                return None if len(result)==0 else result[0]
        def __run(*args,**kwargs):
            if mode=='T':
                return run_threads(*args,**kwargs)
            elif mode=='P':
                return run_processes(*args,**kwargs)
        return __run
    return _run

def locker(func):
    from threading import RLock
    lock=RLock()
    def _lock(*args,**kwargs):
        lock.acquire()
        try:
            return func(*args,**kwargs)
        finally:
            lock.release()
    return _lock

def authorizer(roler,auths=globals()):
    def _authorize(func):
        def __authorize(*args,**kwargs):
            if roler not in auths:
                raise Exception('%s is not authorized.' % roler)
            return func(*args,**kwargs)
        return __authorize
    return _authorize

def logger(msg=None,out=sys.stdout):
    kw=dict(msg=msg,out=out)
    def _log(func):
        msg=kw.get('msg')
        if msg is None or not isinstance(msg,basestring):
            msg="Func '%s' is running.\n" % func.__name__
        elif not msg.endswith('\n'):
            msg=msg+'\n'
        def __log(*args,**kwargs):
            out=kw.get('out')
            out.write(msg)
            return func(*args,**kwargs)
        return __log
    return _log

def profiler(column='time',limit=5):
    from wwpy.util.Module import profile
    def _profile(func):
        def __profile(*args,**kwargs):
            import tempfile
            import pstats
            t=tempfile.mktemp()
            p=profile.Profile()
            p.runcall(func,*args,**kwargs)
            p.dump_stats(t)
            s=pstats.Stats(t)
            s.sort_stats(column).print_stats(limit)
        return __profile
    return _profile

def tracer(func):
    import traceback
    def trace(*args,**kwargs):
        try:
            return func(*args,**kwargs)
        except:
            exc_info=sys.exc_info()
            print exc_info[0],exc_info[1]
            tbs=traceback.extract_tb(exc_info[2])
            for tb in tbs[1:]:
                print '  File "%s", line %s, in %s\n    %s' % tb
    return trace
